// Copyright 2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_UTF16_WIN_H_
#define LIBV_UTF16_WIN_H_

#include <assert.h>
#include <string>
#include <vector>
#include <libv/cast.h>
#include <libv/check.h>
#include <libv/error_msg.h>
#include <libv/windows_lean.h>

#ifndef _WIN32
# error "This header should be used in Windows only"
#endif  // _WIN32



namespace libv {

inline ::std::wstring ToUTF16(const ::std::string& multibyte_string,
                              const int codepage) {
    if ( multibyte_string.empty() ) {
        return ::std::wstring(); 
    }
 
    // Fail if an invalid input character is encountered 
    const DWORD conversion_flags = MB_ERR_INVALID_CHARS; 
    const int mb_string_len = libv::IntegerCast<int>(multibyte_string.size());

    // Get length (in wchar_t's) of resulting UTF-16 string 
    const int utf16_len_int = ::MultiByteToWideChar( 
        codepage,                  // original codepage
        conversion_flags,          // flags 
        multibyte_string.data(),   // source multi byte string 
        mb_string_len,             // length (in chars) of source UTF-8 string 
        NULL,                      // unused - no conversion done in this step 
        0);                        // request size of result, in wchar_t's 

    // Function returns length of the destination string or 0 on error.
    if ( utf16_len_int == 0 ) {
        const DWORD error = ::GetLastError(); 
        ::std::string error_msg = "ToUTF16: ";
        error_msg += libv::OsErrorCode2String(error);
        LIBV_VERIFY_MSG(utf16_len_int != 0, error_msg.c_str());
    } 
 

    const size_t utf16_len = libv::IntegerCast<size_t>(utf16_len_int);
 
    ::std::vector<wchar_t> result_v; 
    result_v.reserve(utf16_len);
    result_v.resize(utf16_len);
 
    const int r = ::MultiByteToWideChar( 
        codepage,                  // original codepage
        0,                         // validation was done in previous call,  
                                   // so speed up things with default flags 
        multibyte_string.data(),   // source multi byte string 
        mb_string_len,             // length (in chars) of source string 
        &result_v[0],              // destination buffer 
        utf16_len_int);            // size of destination buffer, in wchar_t's
    LIBV_VERIFY(static_cast<size_t>(r) == result_v.size());

    return ::std::wstring(result_v.begin(), result_v.end());
} 
 

 
inline ::std::string FromUTF16(const ::std::wstring& utf16_string, 
                               const int codepage){
    if ( utf16_string.empty() ) {  
        return ::std::string();
    }

    const int utf16_string_len = libv::IntegerCast<int>(utf16_string.size());
 
    // Get length (in chars) of resulting string 
    const int result_len_int = ::WideCharToMultiByte( 
        codepage,             // codepage to convert to 
        0,                    // default flags 
        utf16_string.data(),  // source UTF-16 string 
        utf16_string_len,     // source string length, in wchar_t's, 
        NULL,                 // unused - no conversion required in this step 
        0,                    // request buffer size 
        NULL,                 // unused (default chars)
        NULL);                // unused (default chars)

    if ( result_len_int == 0 ) {
        const DWORD error = ::GetLastError(); 
        ::std::string error_msg = "FromUTF16: ";
        error_msg += libv::OsErrorCode2String(error);
        LIBV_VERIFY_MSG(result_len_int != 0, error_msg.c_str());
    }


    const size_t result_len = libv::IntegerCast<size_t>(result_len_int);

    ::std::vector<char> result_v;
    result_v.reserve(result_len);
    result_v.resize(result_len);

    const int r = ::WideCharToMultiByte( 
        codepage,             // codepage to convert to 
        0,                    // default flags 
        utf16_string.data(),  // source UTF-16 string 
        utf16_string_len,     // source string length, in wchar_t's, 
        &result_v[0],         // destination buffer 
        result_len_int,       // destination buffer size, in chars 
        NULL,                 // unused (default chars)
        NULL);                // unused (default chars)
    LIBV_VERIFY(static_cast<size_t>(r) == result_v.size());

    return ::std::string(result_v.begin(), result_v.end());
} 

}  // libv

#endif  // LIBV_UTF16_WIN_H_
